#include "levelscene.h"

LevelScene::LevelScene(QPixmap mapPixmap, QImage mapMask, QWidget *parent)
    : GameScene((QWidget*)parent), endLevelTimer(this)
{
    mainView->setViewport(GLWidget); //use openGL rendering

    // Create the background
    map = new GameBackground(mapPixmap,mainScene);
    QObject::connect(map,SIGNAL(backgroundClicked(QPointF)),this,SLOT(mapClicked(QPointF)));

    // Setup the scene viewport for the size of map we are using
    mainScene->setSceneRect(0,0,map->pixmap().rect().width(),map->pixmap().rect().height() + 150);
    mainScene->setItemIndexMethod(QGraphicsScene::NoIndex);
    mainView->centerView();

    // Setup the tools panel
    panelScene = new QGraphicsScene(0,0,this->width(),150,this);
    panelView = new QGraphicsView(panelScene,this);

    panelView->resize(this->width(),150);
    panelView->move(0,this->height()-150);
    panelView->setStyleSheet("background: transparent");
    panelView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    panelView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    panelView->setFrameStyle(QFrame::NoFrame);

    panelBackground = new GameBackground(QPixmap(RESOURCE_DIR + "/Kit_ToolBarBackground.png"),panelScene);

    currentTool = 0;

    //Make the minimap
    miniMap = new MiniMap(mapPixmap,&buildings,panelScene,mainView);
    miniMap->setPos(width()-miniMap->mapRectToScene(miniMap->pixmap().rect()).width() - 10,10);
    miniMap->screenRect->screenPanned(mainView->getCenterPoint());
    QObject::connect(miniMap->screenRect,SIGNAL(screenMove(QPointF)),mainView,SLOT(screenMoved(QPointF)));
    QObject::connect(mainView,SIGNAL(screenPanned(QPointF)),miniMap->screenRect,SLOT(screenPanned(QPointF)));

    //Setup options panel
    optionsPanel = new OptionsPanel(this);
    optionsPanel->move((width()/2)-(optionsPanel->width()/2),((height()-150)/2)-(optionsPanel->height()/2));
    QObject::connect(optionsPanel,SIGNAL(endScene(GameScene::GameScenes)),this,SLOT(endScene(GameScene::GameScenes)));
    QObject::connect(optionsPanel,SIGNAL(resumeScene()),this,SLOT(resumeScene()));

    statsTimer = new QTimer();
    statsTimer->start(500);
    QObject::connect(statsTimer,SIGNAL(timeout()),this,SLOT(updateStats()));

    stats = new StatsDisplay(panelScene);
    stats->setPos(miniMap->pos().x()-stats->boundingRect().width()-50,10);

    toolCursor = QCursor(QPixmap(RESOURCE_DIR + "/Dean_CursorNonClickable.png"),0,0);
    clickCursor = QCursor(QPixmap(RESOURCE_DIR + "/Dean_CursorClickable.png"),0,0);

    //----------------------------------------------------------------------
    // Initilize the path finding stuff
    //----------------------------------------------------------------------

    pathFinder = new PathFinder(mapMask,this);
    QObject::connect(pathFinder,SIGNAL(pathSolved(std::vector<void*>,QObject*)),this,SLOT(pathSolved(std::vector<void*>,QObject*)));

    //******** Uncomment the following line to draw the passible points on the map
    //visualizer = new PathMapVisualizer(pathFinder->getPathMap(),mainScene);

    // Initialize the level ending variables
    healthyCountThreshold = 50;
    healthyTimeThreshold = QTime(5, 0, 0, 0);
    isThresholdMet = false;
    endLevelTimer.setInterval(100);
    connect(&endLevelTimer, SIGNAL(timeout()), this, SLOT(checkLevelEndState()));
    endLevelTimer.start();
    playTime = QTime();
    maxPeopleOnStreet = 50;
}

void LevelScene::start()
{
    mainView->setPanEnabled(true);
    pathFinder->start();
    people.last()->treatPerson();
    grabKeyboard();
    this->setFocus(Qt::OtherFocusReason);
    stats->setStartPopulation(people.length()*10);
    playTime.start();
    startingPopulation = people.length();

    qDebug("Buildings: %d", buildings.length());
    //for (int index = 0; index < buildings.length(); index++)
    //    mainScene->addRect(buildings.at(index)->getEnterencePoint().x()*10,buildings.at(index)->getEnterencePoint().y()*10,10,10,Qt::NoPen,QBrush(Qt::red,Qt::SolidPattern))->setZValue(4);;

    //Uncomment this if you want to save the buildings as an image
    //saveBuildingImage();

    emit startPeople();
}

void LevelScene::endScene(GameScene::GameScenes nextScene)
{
    mainView->setViewport(0);
    mainView->repaint();
    releaseKeyboard();
    optionsPanel->setEnabled(false,true);
    emit stopPeople();

    GameScene::endScene(nextScene);
    deleteLater();
}

void LevelScene::pauseScene()
{
    panelView->setInteractive(false);
    GameScene::pauseScene();
    QApplication::setOverrideCursor(QCursor(Qt::ArrowCursor));
    PauseTime = QTime();
    PauseTime.start();
    statsTimer->stop();
    emit stopPeople();
}

void LevelScene::resumeScene()
{
    panelView->setInteractive(true);
    GameScene::resumeScene();
    QApplication::restoreOverrideCursor();
    playTime = playTime.addMSecs(PauseTime.elapsed());
    statsTimer->start(500);
    emit startPeople();
}

ToolButton * LevelScene::addTool(int recharge,QString caption, QPixmap toolIcon)
{
    tools.append(new ToolButton(recharge,caption,toolIcon,panelScene));
    tools.last()->setPos(10 + ((tools.length()-1)*55),10);

    QObject::connect(tools.last(),SIGNAL(clicked(ToolButton*)),this,SLOT(toolClicked(ToolButton*)));

    return tools.last();
}

Building * LevelScene::addBuilding(QPoint enterence, QPointF position, QPixmap buildingPixmap)
{
    buildings.append(new Building(enterence,buildingPixmap,mainScene));
    buildings.last()->setPos(position);

    QObject::connect(buildings.last(),SIGNAL(clicked(Building*)),this,SLOT(buildingClicked(Building*)));
    QObject::connect(this,SIGNAL(updateBuildingStats()),buildings.last(),SLOT(updateToolTipData()));
    QObject::connect(buildings.last(),SIGNAL(hoverChanged(bool)),this,SLOT(buildingHoverChanged(bool)));

    return buildings.last();
}

Person * LevelScene::addPerson(QPixmap personPixmap, Building *startingBuilding)
{
    people.append(new Person(pathFinder->getPathMap(), personPixmap, this, mainScene));
    QObject::connect(people.last(),SIGNAL(solvePath(QPoint,QPoint,QObject*)),pathFinder,SLOT(solvePath(QPoint,QPoint,QObject*)));
    QObject::connect(this,SIGNAL(startPeople()),people.last(),SLOT(startPerson()));
    QObject::connect(this,SIGNAL(stopPeople()),people.last(),SLOT(stopPerson()));
    enterBuilding(people.last(),startingBuilding);
    people.last()->setPos(startingBuilding->getEnterencePoint()*10);
    return people.last();
}

void LevelScene::removePerson(Person *person)
{
    mainScene->removeItem(person);
    person->currentBuilding->removePerson(person);
    peopleOnStreet.removeAll(person);
    people.removeAll(person);
    delete person;
}

void LevelScene::pathSolved(std::vector<void *>path, QObject *receiver)
{
    people.at(people.indexOf((Person*)receiver))->setPath(path);
}

void LevelScene::enterBuilding(Person *person, Building *building)
{
    // Add them to the new building
    building->addPerson(person);
    person->setCurrentBuilding(building);

    peopleOnStreet.removeAll(person);
}

bool LevelScene::leaveBuilding(Person *person, Building *building, bool wasBlocked)
{
    if ((peopleOnStreet.length() < maxPeopleOnStreet) || wasBlocked)
    {
        // If the person is currently in a building, remove them
        if ((building != 0) && !wasBlocked)
        {
            building->removePerson(person);
            peopleOnStreet.append(person);
        }
        return true;
    }

    return false;
}

void LevelScene::toolClicked(ToolButton *sender)
{
    if (sender->getSate())
    {
        if (currentTool)
        {
            currentTool->releaseButton(false);
        }
        currentTool = sender;
        QApplication::setOverrideCursor(toolCursor);
    }
    else
    {
        currentTool = 0;
        QApplication::restoreOverrideCursor();
    }
}

void LevelScene::buildingClicked(Building *sender)
{
    if (currentTool != 0)
    {
        if (currentTool == cleanWaterTool)
            return;

        if (currentTool == sterilizationTool)
            Tools::Sterilization(sender);

        if (currentTool == coughSyrupTool)
            Tools::CoughSyrup(sender);

        //Tools for past
        if (currentTool == bonesetExtractTool)
            Tools::Boneset(sender);

        if (currentTool == placeboTool)
            Tools::PlaceboMagic(sender);

        //Tools for present and future
        if (currentTool == antiViralsTool)
            Tools::Antivirals(sender);

        if (currentTool == gammaGlobulinTool)
            Tools::Gammaglobulins(sender);

        if (currentTool == vacceneTool)
            Tools::Vaccene(sender);

        if (currentTool == interferonTool)
            Tools::Interferons(sender);

        //Tools for the future
        if (currentTool == geneTherapyTool)
            Tools::GeneTherapy(sender);

        if (currentTool == nanoBotTherapyTool)
            Tools::NanobotTherapy(sender);

        sender->playToolAnimation();

        currentTool->releaseButton(true);
        QApplication::restoreOverrideCursor();
        currentTool = 0;
    }
}

void LevelScene::buildingHoverChanged(bool isIn)
{
    if ((currentTool != 0) && (currentTool != cleanWaterTool) && (!optionsPanel->isVisible()))
    {
        if (isIn)
            QApplication::changeOverrideCursor(clickCursor);
        else
            QApplication::changeOverrideCursor(toolCursor);
    }
}

void LevelScene::riverHoverChanged(bool isIn)
{
    if ((currentTool == cleanWaterTool) && (!optionsPanel->isVisible()))
    {
        if (isIn)
            QApplication::changeOverrideCursor(clickCursor);
        else
            QApplication::changeOverrideCursor(toolCursor);
    }
}

void LevelScene::mapClicked(QPointF point)
{
    // convert point into a grid position
    point /= 10;

    PathMap * pathMap = pathFinder->getPathMap();

    if ((currentTool == quarantineTool) &&
        (pathMap->IsPointPassable((int)point.x(),(int)point.y())))
    {
        int distance = 1;
        int blockAxis = 0;  // 0 if block none, 1 if block X, 2 if block Y
        for (distance = 1; distance <= 5; distance++)
        {
            if (!pathMap->IsPointPassable((int)point.x()-distance,(int)point.y()) &&
                !pathMap->IsPointPassable((int)point.x()+distance,(int)point.y()))
            {
                blockAxis = 1;
                break;
            }
            else if (!pathMap->IsPointPassable((int)point.x(),(int)point.y()-distance) &&
                     !pathMap->IsPointPassable((int)point.x(),(int)point.y())+distance)
            {
                blockAxis = 2;
                break;
            }
        }

        if (blockAxis == 1)
        {
            for (int count = (int)point.x()-distance; count <= (int)point.x()+distance; count++)
            {
                if (((count-(int)point.x()) % 2 == 0) && (pathMap->IsPointPassable(count,(int)point.y())))
                {
                    roadBlocks.append(new QGraphicsPixmapItem(RESOURCE_DIR + "/RoadBlock.png",0,mainScene));
                    roadBlocks.last()->setOffset(-5,-5);
                    roadBlocks.last()->setPos(count*10,(int)point.y()*10);
                    roadBlocks.last()->setData(ITEM_TYPE,GameItems::RoadBlock);
                }

                pathFinder->setPointPassable(count,(int)point.y(),false);
            }
        }
        else if (blockAxis == 2)
        {
            for (int count = (int)point.y()-distance; count <= (int)point.y()+distance; count++)
            {
                if (((count-(int)point.y()) % 2 == 0) && (pathMap->IsPointPassable((int)point.x(),count)))
                {
                    roadBlocks.append(new QGraphicsPixmapItem(RESOURCE_DIR + "/RoadBlock.png",0,mainScene));
                    roadBlocks.last()->setOffset(-5,-5);
                    roadBlocks.last()->setPos((int)point.x()*10,count*10);
                    roadBlocks.last()->setData(ITEM_TYPE,GameItems::RoadBlock);
                }

                pathFinder->setPointPassable((int)point.x(),count,false);
            }
        }
        else
        {
            pathFinder->setPointPassable((int)point.x(),(int)point.y(),false);

            roadBlocks.append(new QGraphicsPixmapItem(RESOURCE_DIR + "/RoadBlock.png",0,mainScene));
            roadBlocks.last()->setOffset(-5,-5);
            roadBlocks.last()->setPos(((int)point.x())*10,((int)point.y())*10);
            roadBlocks.last()->setData(ITEM_TYPE,GameItems::RoadBlock);
        }

        currentTool->releaseButton(true);
        QApplication::restoreOverrideCursor();
        currentTool = 0;
    }

}

void LevelScene::checkCollisions()
{
    for (int currentPerson = 0; currentPerson < peopleOnStreet.length(); currentPerson++)
    {
        for (int testPerson = (currentPerson+1); testPerson < peopleOnStreet.length(); testPerson++)
        {
            if ((peopleOnStreet.at(currentPerson)->isVisible()) && (peopleOnStreet.at(testPerson)->isVisible()))  // if both people are visible
            {
                if (peopleOnStreet.at(currentPerson)->collidesWithItem(peopleOnStreet.at(testPerson),Qt::IntersectsItemBoundingRect)) // if there is a collision between the two people
                    peopleOnStreet.at(currentPerson)->collidesWithPerson(peopleOnStreet.at(testPerson));
            }
        }
    }

}

void LevelScene::timerEvent(QTimerEvent *event)
{
    GameScene::timerEvent(event);
    checkCollisions();
    if(tools.length() > 0)
        panelScene->advance();
}

void LevelScene::keyPressEvent(QKeyEvent *event)
{
    if (event->key() == Qt::Key_Escape)
    {
        pauseScene();
        if (getLevelEndState() == Playing)
            optionsPanel->setEnabled(true,true);
        else
            optionsPanel->setEnabled(true,false);
    }
    else if (event->key() == Qt::Key_S && event->modifiers() == Qt::ControlModifier)
    {
        if (currentLevel + 1 <= levelScene3)
            endScene((GameScenes)((int)currentLevel + 1));
        else
            endScene(menuScene);
    }
}

void LevelScene::riverClicked()
{
    if(currentTool == cleanWaterTool)
    {
        Tools::CleanWater(buildings);
        cleanWaterTool->releaseButton(true);
        QApplication::restoreOverrideCursor();
    }
}

void LevelScene::updateStats()
{
    emit updateBuildingStats();
    int newInfected = 0, newInfectivity=0;

    for (int index = 0; index < people.length(); index++)
    {
        if (people.at(index)->virus != 0)
        {
            newInfected++;
            newInfectivity+=people.at(index)->virus->Infectivity;
        }
    }

    if (newInfected > 0)    // we need to check the there are infected people before we divide by 0
        stats->setStats(people.length()*10,newInfected*10,(newInfectivity/newInfected),speed,QTime(0,0,0,0).addMSecs(playTime.elapsed()).toString("mm:ss"));
    else
        stats->setStats(people.length()*10,newInfected*10,newInfectivity,speed,QTime(0,0,0,0).addMSecs(playTime.elapsed()).toString("mm:ss"));
    //qDebug("Sterility: %d", buildings.last()->SterilityGetter());
    //qDebug("person H: %f, Person Strengh: %d, virus HP: %f, Virulence: %d",people.last()->stats->getHP(),people.last()->stats->getImmuneStrength(),people.last()->virus->HP,people.last()->virus->Virulence);
}

void LevelScene::saveBuildingImage()
{
    QGraphicsRectItem * mask = mainScene->addRect(mainScene->sceneRect(),QPen(Qt::NoPen),QBrush(QColor(255,0,255),Qt::SolidPattern));
    QImage * saveImage = new QImage((int)mainScene->sceneRect().width(),(int)mainScene->sceneRect().height(),QImage::Format_ARGB32);
    QPainter * painter = new QPainter(saveImage);
    mainScene->render(painter);
    saveImage->save(RESOURCE_DIR + "/scene.png","PNG",-1);
    delete mask;
}

void LevelScene::checkLevelEndState()
{
    switch (getLevelEndState())
    {
    case Won:
    {
        endLevelTimer.stop();
        pauseScene();
        QList<QString> buttons;
        buttons.append("Replay Level");
        buttons.append("Continue");
        Dialog* dialog = new Dialog("Congratulations\n     You won!", buttons, QPixmap(RESOURCE_DIR + "/Kit_EndLevelBackground.png"), this);
        connect(dialog, SIGNAL(buttonClicked(int)), SLOT(wonDialogButtonClicked(int)));
        dialog->move(QPoint(
                QApplication::desktop()->screenGeometry().width() / 2 - dialog->width() / 2,
                QApplication::desktop()->screenGeometry().height() / 2 - dialog->height() / 2));
        dialog->showDialog();
        break;
    }
    case Lost:
    {
        endLevelTimer.stop();
        pauseScene();
        QList<QString> buttons;
        buttons.append("End Level");
        buttons.append("Retry Level");
        Dialog* dialog = new Dialog("Sorry, you lost.\n\nEveryone is infected.", buttons, QPixmap(RESOURCE_DIR + "/Kit_EndLevelBackground.png"), this);
        connect(dialog, SIGNAL(buttonClicked(int)), SLOT(lostDialogButtonClicked(int)));
        dialog->move(QPoint(
                QApplication::desktop()->screenGeometry().width() / 2 - dialog->width() / 2,
                QApplication::desktop()->screenGeometry().height() / 2 - dialog->height() / 2));
        dialog->showDialog();
        break;
    }
    case Playing:
        break;
    }
}

LevelScene::LevelEndState LevelScene::getLevelEndState()
{
    // Count the healthy population
    int healthyCount = 0;

    for(int i = 0; i < people.size(); i++)
    {
        if (people[i]->virus == 0)
            healthyCount++;
    }

    // Lose if everyone is infected
    if (healthyCount < 70 || (double)people.length() / startingPopulation < .4)
        return Lost;

    // Set the start time if it's not already set
    if (healthyCount >= healthyCountThreshold)
    {
        if (!isThresholdMet)
        {
            isThresholdMet = true;
            thresholdMetStartTime.start();
        }
    }
    else
        isThresholdMet = false;

    // Check if both thresholds are met
    QTime zeroTime(0, 0, 0, 0);
    if (isThresholdMet && zeroTime.addMSecs(thresholdMetStartTime.elapsed()) >= healthyTimeThreshold)
        return Won;

    return Playing;
}

void LevelScene::wonDialogButtonClicked(int buttonID)
{
    // Replay level button
    if (buttonID == 0)
        endScene(currentLevel);
    // Continue button
    else if (buttonID == 1)
    {
        if (currentLevel + 1 <= levelScene3)
            endScene((GameScenes)((int)currentLevel + 1));
        else
            endScene(menuScene);
    }
}

void LevelScene::lostDialogButtonClicked(int buttonID)
{
    // End level button
    if (buttonID == 0)
        endScene(menuScene);
    // Retry level button
    else if (buttonID == 1)
        endScene(currentLevel);
}
